/*-------------------------------------------------------------------------
 *
 * xid.c
 *	  POSTGRES 事务标识符和命令标识符数据类型。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/utils/adt/xid.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include <limits.h>

#include "access/multixact.h"
#include "access/transam.h"
#include "access/xact.h"
#include "libpq/pqformat.h"
#include "utils/builtins.h"
#include "utils/xid8.h"

#define PG_GETARG_COMMANDID(n)		DatumGetCommandId(PG_GETARG_DATUM(n))
#define PG_RETURN_COMMANDID(x)		return CommandIdGetDatum(x)


Datum xidin(PG_FUNCTION_ARGS)
{
	char	   *fc_str = PG_GETARG_CSTRING(0);

	PG_RETURN_TRANSACTIONID((TransactionId) strtoul(fc_str, NULL, 0));
}

Datum xidout(PG_FUNCTION_ARGS)
{
	TransactionId fc_transactionId = PG_GETARG_TRANSACTIONID(0);
	char	   *fc_result = (char *) palloc(16);

	snprintf(fc_result, 16, "%lu", (unsigned long) fc_transactionId);
	PG_RETURN_CSTRING(fc_result);
}

/*
 *		xidrecv			- 将外部二进制格式转换为 xid
 */
Datum xidrecv(PG_FUNCTION_ARGS)
{
	StringInfo	fc_buf = (StringInfo) PG_GETARG_POINTER(0);

	PG_RETURN_TRANSACTIONID((TransactionId) pq_getmsgint(fc_buf, sizeof(TransactionId)));
}

/*
 *		xidsend			- 将 xid 转换为二进制格式
 */
Datum xidsend(PG_FUNCTION_ARGS)
{
	TransactionId fc_arg1 = PG_GETARG_TRANSACTIONID(0);
	StringInfoData fc_buf;

	pq_begintypsend(&fc_buf);
	pq_sendint32(&fc_buf, fc_arg1);
	PG_RETURN_BYTEA_P(pq_endtypsend(&fc_buf));
}

/*
 *		xideq			- 两个 xids 是否相等？
 */
Datum xideq(PG_FUNCTION_ARGS)
{
	TransactionId fc_xid1 = PG_GETARG_TRANSACTIONID(0);
	TransactionId fc_xid2 = PG_GETARG_TRANSACTIONID(1);

	PG_RETURN_BOOL(TransactionIdEquals(fc_xid1, fc_xid2));
}

/*
 *		xidneq			- 两个 xids 是否不同？
 */
Datum xidneq(PG_FUNCTION_ARGS)
{
	TransactionId fc_xid1 = PG_GETARG_TRANSACTIONID(0);
	TransactionId fc_xid2 = PG_GETARG_TRANSACTIONID(1);

	PG_RETURN_BOOL(!TransactionIdEquals(fc_xid1, fc_xid2));
}

/*
 *		xid_age			- 计算 XID 的年龄（相对于最新稳定的 xid）
 */
Datum xid_age(PG_FUNCTION_ARGS)
{
	TransactionId fc_xid = PG_GETARG_TRANSACTIONID(0);
	TransactionId fc_now = GetStableLatestTransactionId();

	/* 永久 XIDs 的年龄总是无限大 */
	if (!TransactionIdIsNormal(fc_xid))
		PG_RETURN_INT32(INT_MAX);

	PG_RETURN_INT32((int32) (fc_now - fc_xid));
}

/*
 *		mxid_age			- 计算多 XID 的年龄（相对于最新稳定的 mxid）
 */
Datum mxid_age(PG_FUNCTION_ARGS)
{
	TransactionId fc_xid = PG_GETARG_TRANSACTIONID(0);
	MultiXactId fc_now = ReadNextMultiXactId();

	if (!MultiXactIdIsValid(fc_xid))
		PG_RETURN_INT32(INT_MAX);

	PG_RETURN_INT32((int32) (fc_now - fc_xid));
}

/*
 * xidComparator
 *		qsort 比较函数用于 XIDs
 *
 * 我们不能使用循环比较 XIDs，因为这不尊重
 * 三角不等式！任何旧的排序顺序都可以。
 */
int xidComparator(const void *fc_arg1, const void *fc_arg2)
{
	TransactionId fc_xid1 = *(const TransactionId *) fc_arg1;
	TransactionId fc_xid2 = *(const TransactionId *) fc_arg2;

	if (fc_xid1 > fc_xid2)
		return 1;
	if (fc_xid1 < fc_xid2)
		return -1;
	return 0;
}

/*
 * xidLogicalComparator
 *		qsort 比较函数用于 XIDs
 *
 * 这用于仅比较来自同一纪元的 XIDs（例如，针对同时运行的后端）。
 * 因此，必须只有普通的 XIDs，所以三角不等式没有问题。
 */
int xidLogicalComparator(const void *fc_arg1, const void *fc_arg2)
{
	TransactionId fc_xid1 = *(const TransactionId *) fc_arg1;
	TransactionId fc_xid2 = *(const TransactionId *) fc_arg2;

	Assert(TransactionIdIsNormal(fc_xid1));
	Assert(TransactionIdIsNormal(fc_xid2));

	if (TransactionIdPrecedes(fc_xid1, fc_xid2))
		return -1;

	if (TransactionIdPrecedes(fc_xid2, fc_xid1))
		return 1;

	return 0;
}

Datum xid8toxid(PG_FUNCTION_ARGS)
{
	FullTransactionId fc_fxid = PG_GETARG_FULLTRANSACTIONID(0);

	PG_RETURN_TRANSACTIONID(XidFromFullTransactionId(fc_fxid));
}

Datum xid8in(PG_FUNCTION_ARGS)
{
	char	   *fc_str = PG_GETARG_CSTRING(0);

	PG_RETURN_FULLTRANSACTIONID(FullTransactionIdFromU64(strtou64(fc_str, NULL, 0)));
}

Datum xid8out(PG_FUNCTION_ARGS)
{
	FullTransactionId fc_fxid = PG_GETARG_FULLTRANSACTIONID(0);
	char	   *fc_result = (char *) palloc(21);

	snprintf(fc_result, 21, UINT64_FORMAT, U64FromFullTransactionId(fc_fxid));
	PG_RETURN_CSTRING(fc_result);
}

Datum xid8recv(PG_FUNCTION_ARGS)
{
	StringInfo	fc_buf = (StringInfo) PG_GETARG_POINTER(0);
	uint64		fc_value;

	fc_value = (uint64) pq_getmsgint64(fc_buf);
	PG_RETURN_FULLTRANSACTIONID(FullTransactionIdFromU64(fc_value));
}

Datum xid8send(PG_FUNCTION_ARGS)
{
	FullTransactionId fc_arg1 = PG_GETARG_FULLTRANSACTIONID(0);
	StringInfoData fc_buf;

	pq_begintypsend(&fc_buf);
	pq_sendint64(&fc_buf, (uint64) U64FromFullTransactionId(fc_arg1));
	PG_RETURN_BYTEA_P(pq_endtypsend(&fc_buf));
}

Datum xid8eq(PG_FUNCTION_ARGS)
{
	FullTransactionId fc_fxid1 = PG_GETARG_FULLTRANSACTIONID(0);
	FullTransactionId fc_fxid2 = PG_GETARG_FULLTRANSACTIONID(1);

	PG_RETURN_BOOL(FullTransactionIdEquals(fc_fxid1, fc_fxid2));
}

Datum xid8ne(PG_FUNCTION_ARGS)
{
	FullTransactionId fc_fxid1 = PG_GETARG_FULLTRANSACTIONID(0);
	FullTransactionId fc_fxid2 = PG_GETARG_FULLTRANSACTIONID(1);

	PG_RETURN_BOOL(!FullTransactionIdEquals(fc_fxid1, fc_fxid2));
}

Datum xid8lt(PG_FUNCTION_ARGS)
{
	FullTransactionId fc_fxid1 = PG_GETARG_FULLTRANSACTIONID(0);
	FullTransactionId fc_fxid2 = PG_GETARG_FULLTRANSACTIONID(1);

	PG_RETURN_BOOL(FullTransactionIdPrecedes(fc_fxid1, fc_fxid2));
}

Datum xid8gt(PG_FUNCTION_ARGS)
{
	FullTransactionId fc_fxid1 = PG_GETARG_FULLTRANSACTIONID(0);
	FullTransactionId fc_fxid2 = PG_GETARG_FULLTRANSACTIONID(1);

	PG_RETURN_BOOL(FullTransactionIdFollows(fc_fxid1, fc_fxid2));
}

Datum xid8le(PG_FUNCTION_ARGS)
{
	FullTransactionId fc_fxid1 = PG_GETARG_FULLTRANSACTIONID(0);
	FullTransactionId fc_fxid2 = PG_GETARG_FULLTRANSACTIONID(1);

	PG_RETURN_BOOL(FullTransactionIdPrecedesOrEquals(fc_fxid1, fc_fxid2));
}

Datum xid8ge(PG_FUNCTION_ARGS)
{
	FullTransactionId fc_fxid1 = PG_GETARG_FULLTRANSACTIONID(0);
	FullTransactionId fc_fxid2 = PG_GETARG_FULLTRANSACTIONID(1);

	PG_RETURN_BOOL(FullTransactionIdFollowsOrEquals(fc_fxid1, fc_fxid2));
}

Datum xid8cmp(PG_FUNCTION_ARGS)
{
	FullTransactionId fc_fxid1 = PG_GETARG_FULLTRANSACTIONID(0);
	FullTransactionId fc_fxid2 = PG_GETARG_FULLTRANSACTIONID(1);

	if (FullTransactionIdFollows(fc_fxid1, fc_fxid2))
		PG_RETURN_INT32(1);
	else if (FullTransactionIdEquals(fc_fxid1, fc_fxid2))
		PG_RETURN_INT32(0);
	else
		PG_RETURN_INT32(-1);
}

Datum xid8_larger(PG_FUNCTION_ARGS)
{
	FullTransactionId fc_fxid1 = PG_GETARG_FULLTRANSACTIONID(0);
	FullTransactionId fc_fxid2 = PG_GETARG_FULLTRANSACTIONID(1);

	if (FullTransactionIdFollows(fc_fxid1, fc_fxid2))
		PG_RETURN_FULLTRANSACTIONID(fc_fxid1);
	else
		PG_RETURN_FULLTRANSACTIONID(fc_fxid2);
}

Datum xid8_smaller(PG_FUNCTION_ARGS)
{
	FullTransactionId fc_fxid1 = PG_GETARG_FULLTRANSACTIONID(0);
	FullTransactionId fc_fxid2 = PG_GETARG_FULLTRANSACTIONID(1);

	if (FullTransactionIdPrecedes(fc_fxid1, fc_fxid2))
		PG_RETURN_FULLTRANSACTIONID(fc_fxid1);
	else
		PG_RETURN_FULLTRANSACTIONID(fc_fxid2);
}

/*****************************************************************************
 *	 命令标识符例程											 *
 *****************************************************************************/

/*
 *		cidin	- 将 CommandId 转换为内部表示。
 */
Datum cidin(PG_FUNCTION_ARGS)
{
	char	   *fc_str = PG_GETARG_CSTRING(0);

	PG_RETURN_COMMANDID((CommandId) strtoul(fc_str, NULL, 0));
}

/*
 *		cidout	- 将 cid 转换为外部表示。
 */
Datum cidout(PG_FUNCTION_ARGS)
{
	CommandId	fc_c = PG_GETARG_COMMANDID(0);
	char	   *fc_result = (char *) palloc(16);

	snprintf(fc_result, 16, "%lu", (unsigned long) fc_c);
	PG_RETURN_CSTRING(fc_result);
}

/*
 *		cidrecv			- 将外部二进制格式转换为 cid
 */
Datum cidrecv(PG_FUNCTION_ARGS)
{
	StringInfo	fc_buf = (StringInfo) PG_GETARG_POINTER(0);

	PG_RETURN_COMMANDID((CommandId) pq_getmsgint(fc_buf, sizeof(CommandId)));
}

/*
 *		cidsend			- 将 cid 转换为二进制格式
 */
Datum cidsend(PG_FUNCTION_ARGS)
{
	CommandId	fc_arg1 = PG_GETARG_COMMANDID(0);
	StringInfoData fc_buf;

	pq_begintypsend(&fc_buf);
	pq_sendint32(&fc_buf, fc_arg1);
	PG_RETURN_BYTEA_P(pq_endtypsend(&fc_buf));
}

Datum cideq(PG_FUNCTION_ARGS)
{
	CommandId	fc_arg1 = PG_GETARG_COMMANDID(0);
	CommandId	fc_arg2 = PG_GETARG_COMMANDID(1);

	PG_RETURN_BOOL(fc_arg1 == fc_arg2);
}
